package utils

import (
	"log"
	"os"
	"time"

	"github.com/golang-jwt/jwt/v5"
)

type JWTAuth struct{}

type BaseClaims struct {
	ApplicationTag string //所处的应用
	DbName         string //所属的租户
	Domain         string //所属的租户名称
	UUID           string //用户uuid
}

type CustomClaims struct {
	BaseClaims
	jwt.RegisteredClaims
}

// 生成Claims
func (j *JWTAuth) CreateClaims(baseClaims BaseClaims, expire uint) *CustomClaims {
	return &CustomClaims{
		BaseClaims: baseClaims,
		RegisteredClaims: jwt.RegisteredClaims{
			NotBefore: jwt.NewNumericDate(time.Now().Add(-time.Second)),
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expire) * time.Second)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Subject:   "",         //用于控制租户数据库隔离
			Audience:  []string{}, //用于控制权限资源
		},
	}
}

// 生成token
func (j *JWTAuth) GenTokenString(claims *CustomClaims) string {
	// 读取公钥
	privateKeyBytes, err := os.ReadFile("source/auth/private.pem")
	if err != nil {
		panic(err)
	}
	privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(privateKeyBytes)
	if err != nil {
		panic(err)
	}
	token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
	singedToken, err := token.SignedString(privateKey)
	if err != nil {
		panic(err)
	}
	return singedToken
}

// 解析token
func (j *JWTAuth) ParseToken(tokenString string) (*CustomClaims, error) {
	// 读取公钥
	publicKeyBytes, err := os.ReadFile("source/auth/public.pem")
	if err != nil {
		return nil, err
	}
	publicKey, err := jwt.ParseRSAPublicKeyFromPEM(publicKeyBytes)
	if err != nil {
		return nil, err
	}
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(t *jwt.Token) (interface{}, error) {
		return publicKey, nil
	})
	if err != nil {
		log.Fatal(err)
		if jwt.ErrTokenMalformed == err {
			return nil, jwt.ErrTokenMalformed
		} else if jwt.ErrTokenExpired == err {
			return nil, jwt.ErrTokenExpired
		} else if jwt.ErrTokenNotValidYet == err {
			return nil, jwt.ErrTokenNotValidYet
		}
	}
	if token != nil {
		if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
			return claims, nil
		}
		return nil, jwt.ErrTokenInvalidClaims
	} else {
		return nil, jwt.ErrTokenInvalidClaims
	}
}
